#if defined(_WIN32) || defined(_WIN64)
#include <winsock2.h>
#elif defined(__linux__)
#include <sys/socket.h>
#else
#error "not support platform"
#endif


#include "Console.h"
#include "Http.h"
#include "HttpClient.h"
#include "HttpFixedResponse.h"
#include "HttpRequest.h"
#include "HttpResponse.h"
#include "Route.h"
#include "Server.h"

#include <string.h>
#include <unistd.h>
#include "./common/YXConfig.h"
#include "./common/YXConstant.h"
#include "./common/YXIO.h"
#include "./common/YXJwt.h"
#include "./common/YXLog.h"

static Server server;

static void console_accept_request(EventLoop *el, int fd, int mask, void *client_data);
static void console_read_reqeust(EventLoop *el, int fd, int mask, void *client_data);
static void console_parse_request(EventLoop *el, Client *client);
static int  console_parse_http_request_body(EventLoop *el, Client *client);
static void console_do_request(EventLoop *el, Client *client);

void        console_generate_response(Client *client);
static void console_do_response(EventLoop *el, Client *client);
static void console_send_response(EventLoop *el, int fd, int mask, void *client_data);

static int console_get_file(Request *request, Response *response);

static Client *console_create_client(int client_fd);
static void    console_free_client(Client *client);
static void    console_reset_client(Client *client);

void console_accept_request(EventLoop *el, int fd, int mask, void *client_data) {
    struct sockaddr_in client_address;
    int                addrlen   = sizeof(client_address);
    int                client_fd = accept(fd, (struct sockaddr *) &client_address, &addrlen);
    if (client_fd == -1) {
        logErrno();
        return;
    }
    if (console_create_client(client_fd) == NULL) {
        close(client_fd);
        return;
    }
    logDebug("A new client connected: %s:%d", inet_ntoa(client_address.sin_addr), ntohs(client_address.sin_port));
}

/**
 * 读取客户端请求
 */
void console_read_reqeust(EventLoop *el, int fd, int mask, void *client_data) {
    int      n;
    Client  *client  = client_data;
    Request *request = client->request;

    while (1) {
        if (request->state == HTTP_REQUEST_STATE_PARSE_LINE || request->state == HTTP_REQUEST_STATE_PARSE_HEADER) {
            n = read_line(fd, client->input_buffer);
            if (n == READ_SUCCESS) {
                console_parse_request(el, client);
                if (request->state == HTTP_REQUEST_STATE_PARSE_COMPLETED)
                    break;
                continue;
            }
            if (n == READ_UNABLE) {
                send_response_bad_request(el, client, "The line of request is too long");
                return;
            }
        } else {
            n = read_all(fd, &request->body, request->content_length);
            if (n == READ_SUCCESS) {
                console_parse_request(el, client);
                if (request->state == HTTP_REQUEST_STATE_PARSE_COMPLETED)
                    break;
            }
        }
        if (n == READ_EXIT || n == READ_FAILED) {
            console_free_client(client);
        }
        return;
    }
}

void console_parse_request(EventLoop *el, Client *client) {
    Request *request = client->request;

    switch (request->state) {
        case HTTP_REQUEST_STATE_PARSE_LINE: {
            if (parse_http_request_line(el, client) == YX_OK) {
                request->state = HTTP_REQUEST_STATE_PARSE_HEADER;
            }
            break;
        }
        case HTTP_REQUEST_STATE_PARSE_HEADER: {
            if (parse_http_request_header(el, client) == YX_OK) {
                if (request->content_length > 0) {
                    request->state = HTTP_REQUEST_STATE_PARSE_BODY;
                } else {
                    net_delete_file_event(el, client->fd, YX_NET_READABLE);
                    request->state = HTTP_REQUEST_STATE_PARSE_COMPLETED;
                    console_do_request(el, client);
                    console_do_response(el, client);
                }
            }
            break;
        }
        case HTTP_REQUEST_STATE_PARSE_BODY: {
            if (console_parse_http_request_body(el, client) == YX_OK) {
                net_delete_file_event(el, client->fd, YX_NET_READABLE);
                request->state = HTTP_REQUEST_STATE_PARSE_COMPLETED;
                console_do_request(el, client);
                console_do_response(el, client);
            }
            break;
        }
        default: {
        }
    }
}

int console_parse_http_request_body(EventLoop *el, Client *client) {
    if (sds_len(client->request->body) < client->request->content_length) {
        return YX_ERR;
    }
    return YX_OK;
}

void console_do_request(EventLoop *el, Client *client) {
    Request  *request  = client->request;
    Response *response = client->response;
    // 鉴权
    if (strncmp(request->path, "/op", 3) == 0 && strcmp(request->path + 3, "/login") != 0) {
        Claims *claims;
        if (sds_len(request->token) == 0 || (claims = yx_jwt_parse(request->token)) == NULL) {
            set_response_unauthorized(response);
            return;
        }
        yx_jwt_claims_free(claims);
    }

    // 处理静态文件
    if (strcmp(request->path, "/") == 0) {
        sds_cat(request->path, "static/index.html");
    }
    if (strncmp(request->path, "/static", 7) == 0) {
        console_get_file(request, response);
        return;
    }

    EnhancedRouteHandler *enhancedHandler = NULL;
    if (strcmp(request->method, "GET") == 0) {
        enhancedHandler = getGetRouteHandler(request->path);
    } else if (strcmp(request->method, "POST") == 0) {
        enhancedHandler = getPostRouteHandler(request->path);
    } else if (strcmp(request->method, "PUT") == 0) {
        enhancedHandler = getPutRouteHandler(request->path);
    } else if (strcmp(request->method, "DELETE") == 0) {
        enhancedHandler = getDeleteRouteHandler(request->path);
    }
    if (enhancedHandler) {
        // 首先执行beforeRouterHandler
        BeforeRouteHandlerWarpper *beforeRouterHandlerWarpper = enhancedHandler->beforeRouteHandlerLinkedList;
        while (beforeRouterHandlerWarpper) {
            YXResult *result =
                    beforeRouterHandlerWarpper->handler(request, response, beforeRouterHandlerWarpper->privateData);
            if (result) {
                set_response_body(response, cJSON_PrintUnformatted(result));
                yxResult_free(result);
                return;
            }
            beforeRouterHandlerWarpper = beforeRouterHandlerWarpper->next;
        }

        YXResult *result = enhancedHandler->mainHandler(request, response);
        set_response_body(response, cJSON_PrintUnformatted(result));
        yxResult_free(result);
        return;
    }
    set_response_not_found(response);
}

void console_generate_response(Client *client) {
    Response *response = client->response;

    int n;
    sds buf = client->input_buffer;
    sds_clear(buf);
    // 写入状态行
    sds_cat_printf(buf, "HTTP/1.0 %d %s\r\n", response->staus, response->staus_title);
    // 写入响应头
    sds_cat_len(buf, "Server: MT/0.0.1\r\nConnection: close\r\n", 37);
    sds_cat_printf(buf, "Content-Type: %s\r\n", response->content_type);

    if (response->content_length == -1) {
        response->content_length = sds_len(response->body);
    }
    n = sprintf(buf + sds_len(buf), "Content-Length: %d\r\n", response->content_length);
    sds_incr_len(buf, n);

    // 控制层设置的响应头
    LinkedListIterator *iterator = linked_list_iterator_create(response->headers);
    if (iterator) {
        Node   *node   = NULL;
        Header *header = NULL;
        while ((node = linked_list_iterator_next(iterator)) != NULL) {
            header = (Header *) node->value;
            n      = sprintf(buf + sds_len(buf), "%s: %s\r\n", header->key, header->value);
            sds_incr_len(buf, n);
        }
        linked_list_iterator_free(iterator);
    }

    sds_cat_len(buf, "\r\n", 2);
}

void console_do_response(EventLoop *el, Client *client) {
    console_generate_response(client);
    net_create_file_event(el, client->fd, YX_NET_WRITABLE, console_send_response, client);
}

/**
 * 发送响应给客户端
 */
void console_send_response(EventLoop *el, int fd, int mask, void *client_data) {
    int       n;
    Client   *client   = client_data;
    Response *response = client->response;
    sds       buf      = client->input_buffer;

    // 发送状态行和响应头
    if (sds_len(buf)) {
        n = send(fd, buf, sds_len(buf), 0);
        if (n == 0) {
            console_free_client(client);
        } else if (n == -1) {
            if (errno != EAGAIN) {
                console_free_client(client);
            }
        } else {
            client->bufpos += n;
            sds_update_len(buf, sds_len(buf) - n);
        }
        return;
    }

    // 写入响应体
    if (sds_len(response->body)) {
        n = send(fd, response->body + response->bodypos, sds_len(response->body), 0);
        if (n == 0) {
            console_free_client(client);
            return;
        } else if (n == -1) {
            if (errno != EAGAIN) {
                console_free_client(client);
            }
            return;
        } else {
            response->bodypos += n;
            sds_update_len(response->body, sds_len(response->body) - n);
        }
    }
    if (sds_len(response->body) == 0) {
        console_free_client(client);
    }
}

int console_get_file(Request *request, Response *response) {
    if (get_file_basic_info("..", request, response) == YX_ERR) {
        return YX_ERR;
    }

    char *full_path = malloc(HTTP_REQUEST_FULL_PATH_MAX_LENGTH);
    sprintf(full_path, "%s%s", "..", request->path);

    if (file_read_all(full_path, &response->body) == READ_FAILED) {
        free(full_path);
        return YX_ERR;
    }

    free(full_path);
    return YX_OK;
}

Client *console_create_client(int client_fd) {
    // 设置非阻塞
    if (net_set_non_block(client_fd) == YX_ERR) {
        logErrno();
        close(client_fd);
        return NULL;
    }

    Client *client = malloc(sizeof(Client));

    // 添加文件事件
    if (net_create_file_event(server.el, client_fd, YX_NET_READABLE, console_read_reqeust, client) == YX_ERR) {
        logErrno();
        close(client_fd);
        free(client);
        return NULL;
    }

    client->auth         = 0;
    client->fd           = client_fd;
    client->bufpos       = 0;
    client->input_buffer = sds_create_empty(HTTP_TEMP_INPUT_BUFFER_SIZE);
    client->request      = create_request();
    client->response     = create_response();
    client_list_add(server.client_list, client);

    return client;
}

void console_free_client(Client *client) {
    net_delete_file_event(server.el, client->fd, server.el->events[client->fd].mask);
    close(client->fd);
    client_list_remove(server.client_list, client);
    sds_free(client->input_buffer);
    free_request(client->request);
    free_response(client->response);
    free(client);
    logDebug("A client closed connection");
}

void console_reset_client(Client *client) {
    net_create_file_event(server.el, client->fd, YX_NET_READABLE, console_read_reqeust, client);
    client->bufpos = 0;
    sds_clear(client->input_buffer);
    reset_request(client->request);
    reset_response(client->response);
}

static void init_console() {
    server.el = net_create_event_loop(yxConfig.chooseIntProperty("server.max-clients", SERVER_DEFAULT_MAX_CLIENTS) +
                                      EVENTLOOP_FDSET_INCR);
    server.client_list = client_list_create();

    server.fd = net_create_server_socket(yxConfig.chooseStrProperty("console.host", SERVER_DEFAULT_HOST),
                                         yxConfig.chooseIntProperty("console.port", SERVER_DEFAULT_PORT));
    if (net_create_file_event(server.el, server.fd, YX_NET_READABLE, console_accept_request, NULL) == YX_ERR) {
        logErrno();
        exit(EXIT_FAILURE);
    }
}

static void loop(EventLoop *el) {
    while (!el->stop) {
        int nums = yxApiPoll(el, -1);
        for (int i = 0; i < nums; i++) {
            FileEvent *file_event = &el->events[el->fired_events[i].fd];
            int        fd         = el->fired_events[i].fd;
            int        mask       = el->fired_events[i].mask;

            if (file_event->mask & mask & YX_NET_READABLE) {
                file_event->read_proc(el, fd, mask, file_event->client_data);
            }
            if (file_event->mask & mask & YX_NET_WRITABLE) {
                file_event->write_proc(el, fd, mask, file_event->client_data);
            }
        }
    }
}

void start_console() {
    init_console();
    // 开启新线程
    pthread_t tid;
    if (pthread_create(&tid, NULL, (void *) loop, server.el) != 0) {
        logErrno();
        exit(EXIT_FAILURE);
    }
}
